//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include <math.h>
#include <Math/Vector.hpp>
#include <Rendering/OpenGL.hpp>
#include <Geometry/PredefinedShape.hpp>

namespace Geometry{

	//-----------------------------------------------------------------------------
	void DrawGrid( int gridSize ) {
		int boldLineIndex = 5;
		glPushAttrib(GL_COLOR_BUFFER_BIT);
		// Specification de la couleur a utiliser
		glColor3f(1.f,1.f,1.f);
		// Dessing des colonnes
		for( int indexLine = -gridSize; indexLine <= gridSize; ++indexLine ) {
			if(indexLine%boldLineIndex==0)
				glPointSize(2.f);
			glBegin( GL_LINES );
			glVertex3i( indexLine, 0, -gridSize );
			glVertex3i( indexLine, 0,  gridSize );
			glEnd();
			if(indexLine%boldLineIndex==0)
				glPointSize(1.f);
		}
		// Dessin des lignes
		for( int indexLine = -gridSize; indexLine <= gridSize; ++indexLine ) {
			if(indexLine%boldLineIndex==0)
				glPointSize(2.f);
			glBegin( GL_LINES );
			glVertex3i( -gridSize, 0, indexLine);
			glVertex3i(  gridSize, 0, indexLine);
			glEnd();
			if(indexLine%boldLineIndex==0)
				glPointSize(1.f);
		}
		glPopAttrib();
	}
	//-----------------------------------------------------------------------------
	void DrawMaterial( const float* color ) {
		// Evaluate the reflective properties of the material
		float colorRGBA[] = { color[0], color[1], color[2], 1.0f };
		glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, colorRGBA);
	}	
	//-----------------------------------------------------------------------------
	void DrawHemisphere( int nTheta, int nPhi, const std::vector<float>& weights ) {
		glPushAttrib(GL_COLOR_BUFFER_BIT);
// 		// Preprocessing on weigth to normalize it
// 		int nPoints, maxIndex;
// 		nPoints = nPhi*nTheta;
// 		maxIndex = 0;	
// 		for(int weightIndex=0; weightIndex<nPoints; ++weightIndex) {
// 			if( weights[maxIndex] < weights[weightIndex] )
// 				maxIndex = weightIndex;
// 		}
// 		float normalizationValue = weights[maxIndex];
// 		for(int weightIndex=0; weightIndex<nPoints; ++weightIndex) {
// 			weights[weightIndex] /= normalizationValue;
// 		}
		// Compute & render hemisphere points
		float deltaPhi, deltaTheta;
		deltaTheta = (PI/2.f)/(float)nTheta;
		deltaPhi = (2.f*PI)/(float)nPhi;
		float origin[3];
		Vector::Init3(origin,0.f,0.f,0.f);
		for(int phiIndex=0; phiIndex<nPhi; ++phiIndex) {
			for(int thetaIndex=0; thetaIndex<nTheta; ++thetaIndex) {
				// Compute basic indices of strata
				float theta, phi;
				theta = deltaTheta*thetaIndex;
				phi = deltaPhi*phiIndex;
				// Generate the four points of the strata
				float point0[3], point1[3], point2[3], point3[3];
				SphericalDirection( sinf(theta), cosf(theta), phi, point0);
				SphericalDirection( sinf(theta), cosf(theta), phi+deltaPhi, point1);
				SphericalDirection( sinf(theta+deltaTheta), cosf(theta+deltaTheta), phi+deltaPhi, point2);
				SphericalDirection( sinf(theta+deltaTheta), cosf(theta+deltaTheta), phi, point3);
				// Weighting points			
				float currentWeight = weights[StrataIndex(nTheta,nPhi,thetaIndex,phiIndex)];
				Vector::ConstMul3(point0, currentWeight, point0);
				Vector::ConstMul3(point1, currentWeight, point1);
				Vector::ConstMul3(point2, currentWeight, point2);
				Vector::ConstMul3(point3, currentWeight, point3);
	
				// Generate normal of the strata
				float normal0[3], normal1[3], normal2[3], normal3[3], normal4[3];
				ComputeNormal(normal0, point0, point1, point3);
				ComputeNormal(normal1, point2, point3, origin);
				ComputeNormal(normal2, point1, point0, origin);
				ComputeNormal(normal3, point2, point1, origin);
				ComputeNormal(normal4, point3, point2, origin);
				// Render triangle
				glEnable(GL_LIGHTING);
				glColor3f( 1.f,1.f,1.f );
				glBegin( GL_QUADS );
					glNormal3f( normal0[0], normal0[1], normal0[2] );
					glVertex3f( point0[0], point0[1], point0[2] );
					glVertex3f( point1[0], point1[1], point1[2] );
					glVertex3f( point2[0], point2[1], point2[2] );
					glVertex3f( point3[0], point3[1], point3[2] );
				glEnd();
				glBegin( GL_TRIANGLES );
					glNormal3f( normal1[0], normal1[1], normal1[2] );
					glVertex3f( point0[0], point0[1], point0[2] );
					glVertex3f( point3[0], point3[1], point3[2] );
					glVertex3f( origin[0], origin[1], origin[2] );
	
					glNormal3f( normal2[0], normal2[1], normal2[2] );
					glVertex3f( point1[0], point1[1], point1[2] );
					glVertex3f( point0[0], point0[1], point0[2] );
					glVertex3f( origin[0], origin[1], origin[2] );
					
					glNormal3f( normal3[0], normal3[1], normal3[2] );
					glVertex3f( point2[0], point2[1], point2[2] );
					glVertex3f( point1[0], point1[1], point1[2] );
					glVertex3f( origin[0], origin[1], origin[2] );
	
					glNormal3f( normal4[0], normal4[1], normal4[2] );
					glVertex3f( point3[0], point3[1], point3[2] );
					glVertex3f( point2[0], point2[1], point2[2] );
					glVertex3f( origin[0], origin[1], origin[2] );
				glEnd();			
				// Render Wireframe
				glEnable (GL_POLYGON_OFFSET_FILL);
				glPolygonOffset (1., 1.);
				glDisable(GL_LIGHTING);
				//glColor3f( 0.5f,0.5f,0.5f );
				glBegin( GL_LINES );
					glVertex3f( point0[0], point0[1], point0[2] );
					glVertex3f( point1[0], point1[1], point1[2] );
	
					glVertex3f( point1[0], point1[1], point1[2] );
					glVertex3f( point2[0], point2[1], point2[2] );
	
					glVertex3f( point2[0], point2[1], point2[2] );
					glVertex3f( point3[0], point3[1], point3[2] );
	
					glVertex3f( point3[0], point3[1], point3[2] );
					glVertex3f( point0[0], point0[1], point0[2] );
	
					glVertex3f( point0[0], point0[1], point0[2] );
					glVertex3f( origin[0], origin[1], origin[2] );
	
					glVertex3f( point1[0], point1[1], point1[2] );
					glVertex3f( origin[0], origin[1], origin[2] );
	
					glVertex3f( point2[0], point2[1], point2[2] );
					glVertex3f( origin[0], origin[1], origin[2] );
	
					glVertex3f( point3[0], point3[1], point3[2] );
					glVertex3f( origin[0], origin[1], origin[2] );				
				glEnd();
			}
		}
		glPopAttrib();
		
	}
	//---------------------------------------------------------------------
	void DrawReferential( float size ) {
		glPushAttrib(GL_COLOR_BUFFER_BIT);
		glLineWidth(2.0f);
		glBegin(GL_LINES);
			// X axis
			glColor3f(1.f,0.f,0.f);
			glVertex3f(0.f,0.f,0.f);
			glVertex3f(size,0.f,0.f);
			// Y axis
			glColor3f(0.f,1.f,0.f);
			glVertex3f(0.f,0.f,0.f);
			glVertex3f(0.f,size,0.f);
			// Z axis
			glColor3f(0.f,0.f,1.f);
			glVertex3f(0.f,0.f,0.f);
			glVertex3f(0.f,0.f,size);
		glEnd();
		glLineWidth(1.0f);
		glPopAttrib();
	}
	//---------------------------------------------------------------------
	void DrawPoint(const float* position, float size) {
		glPointSize(size);
		glBegin(GL_POINTS);
		glVertex3f( position[0],position[1],position[2] );
		glEnd();	
		glPointSize(1.0f);		
	}
	//---------------------------------------------------------------------
	void DrawLine(const float* s, const float* e, float size) {
		glLineWidth(size);
		glBegin(GL_LINES);
		glColor3f( 1.f,1.f,1.f );
		glVertex3f( s[0],s[1],s[2] );
		glVertex3f( e[0],e[1],e[2] );		
		glEnd();
		glLineWidth(1.0f);
	}
	//---------------------------------------------------------------------
	void Draw2DRectangle( int start[2], int end[2] ) {		
		glBegin(GL_QUADS);
		glColor4f(0.27f,0.27f,0.27f,1.f);
		glVertex2i(start[0], start[1]);
		glColor4f(0.f,0.f,0.f,0.5f);
		glVertex2i(end[0], start[1]);
		glColor4f(0.f,0.f,0.f,0.5f);
		glVertex2i(end[0], end[1]);
		glColor4f(0.27f,0.27f,0.27f,1.f);
		glVertex2i(start[0], end[1]);
		glEnd();
	}
}
